package com.isyscore.os.metadata.service.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.isyscore.os.metadata.enums.KettleDataFlowNodeGroupType;
import com.isyscore.os.metadata.enums.KettleDataFlowNodeType;
import com.isyscore.os.metadata.kettle.base.FlowMetaCodec;
import com.isyscore.os.metadata.kettle.dto.FlowNodeValidatedResultDTO;
import com.isyscore.os.metadata.kettle.vis.FlowParser;
import com.isyscore.os.metadata.kettle.vis.VisGraph;
import com.isyscore.os.metadata.kettle.vis.VisNode;
import com.isyscore.os.metadata.model.dto.NodeNotAllowsDTO;
import com.isyscore.os.metadata.service.ETLFlowNodeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Service
@RequiredArgsConstructor
@Slf4j
public class ETLFlowNodeServiceImpl implements ETLFlowNodeService {
    private static final String NODE_TYPE_PROP_NAME = "nodeType";
    private static final String NODE_NAME = "nodeName";
    private static final String ID = "id";
    private Map<KettleDataFlowNodeType, Class<? extends VisNode>> nodeParserMap = Maps.newHashMap();
    private final FlowParser flowParser;
    @Autowired
    FlowMetaCodec codec;

    @Override
    public List<NodeNotAllowsDTO> accept() {
        //反射获取VisNode的所有实现类
        Reflections reflections = new Reflections(VisNode.class.getPackage().getName());
        Set<Class<? extends VisNode>> implClass = reflections.getSubTypesOf(VisNode.class);
        List<KettleDataFlowNodeType> inputs = Lists.newArrayList();
        List<KettleDataFlowNodeType> trans = Lists.newArrayList();
        List<KettleDataFlowNodeType> outputs = Lists.newArrayList();
        //遍历实现类获取所有的节点类型和节点分组类型
        for (Class<? extends VisNode> node : implClass) {
            try {
                Method groupTypeMethod = node.getMethod("getGroupType");
                Object nodeObj = node.newInstance();
                KettleDataFlowNodeGroupType groupType = (KettleDataFlowNodeGroupType) groupTypeMethod.invoke(nodeObj);
                Method method = node.getMethod("getNodeType");
                KettleDataFlowNodeType getNodeTypeMethod = (KettleDataFlowNodeType) method.invoke(nodeObj);
                if (KettleDataFlowNodeGroupType.INPUTS.equals(groupType)) {
                    inputs.add(getNodeTypeMethod);
                } else if (KettleDataFlowNodeGroupType.TRANS.equals(groupType)) {
                    trans.add(getNodeTypeMethod);
                } else if (KettleDataFlowNodeGroupType.OUTPUTS.equals(groupType)) {
                    outputs.add(getNodeTypeMethod);
                }
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException |
                     InstantiationException e) {
                throw new RuntimeException(e);
            }
        }
        List<NodeNotAllowsDTO> notAllowsDTOs = Lists.newArrayList();
        // 转换 -> 表输入
        for (KettleDataFlowNodeType tran : trans) {
            for (KettleDataFlowNodeType input : inputs) {
                notAllowsDTOs.add(NodeNotAllowsDTO.builder().sourceNodeType(tran.name()).targetNodeType(input.name()).build());
            }
        }

        // 表输出,插入更新 ,脚本-> 所有
        for (KettleDataFlowNodeType output : outputs) {
            for (KettleDataFlowNodeType input : inputs) {
                notAllowsDTOs.add(NodeNotAllowsDTO.builder().sourceNodeType(output.name()).targetNodeType(input.name()).build());
            }
            for (KettleDataFlowNodeType tran : trans) {
                notAllowsDTOs.add(NodeNotAllowsDTO.builder().sourceNodeType(output.name()).targetNodeType(tran.name()).build());
            }
            for (KettleDataFlowNodeType outputTo : outputs) {
                notAllowsDTOs.add(NodeNotAllowsDTO.builder().sourceNodeType(output.name()).targetNodeType(outputTo.name()).build());
            }
        }
        return notAllowsDTOs;
    }

    @Override
    public List<FlowNodeValidatedResultDTO> validateGraph(VisGraph userConfig) {
        List<FlowNodeValidatedResultDTO> flowNodeValidatedResultDTOS = Lists.newArrayList();
        for (Map<String, Object> map : userConfig.getNodes()) {
            String msg = "";
            try {
                if (ObjectUtil.isNull(map.get(ID))) {
                    msg="节点存在空ID";
                }
                if (ObjectUtil.isNull(map.get(NODE_NAME))) {
                    msg="节点存在空名字";
                }
                String validateMsg = flowParser.validateNode(map);
                if(StrUtil.isNotBlank(validateMsg)){
                    msg=validateMsg;
                }
                //尝试转码为xml，对于转换中的异常添加到结果中
//                if(!StrUtil.isNotBlank(msg)){
//                    FlowConfig flowConfig = flowParser.parseFlow(userConfig, "test1");
//                    codec.decode(flowConfig).getXML();
//                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                msg = "节点校验发生异常"+ e.getMessage();
            } finally {
                if (StrUtil.isNotBlank(msg)) {
                    flowNodeValidatedResultDTOS.add(FlowNodeValidatedResultDTO.builder().nodeId(
                            ObjectUtil.isNotNull(map.get(ID))? String.valueOf(map.get(ID)):""
                    ).nodeName(
                            ObjectUtil.isNotNull(map.get(NODE_NAME))? String.valueOf(map.get(NODE_NAME)):""
                    ).errorMsg(msg).passCheck(Boolean.FALSE).build());
                } else {
                    flowNodeValidatedResultDTOS.add(FlowNodeValidatedResultDTO.builder().nodeId(
                            ObjectUtil.isNotNull(map.get(ID))? String.valueOf(map.get(ID)):""
                    ).nodeName(
                            ObjectUtil.isNotNull(map.get(NODE_NAME))? String.valueOf(map.get(NODE_NAME)):""
                    ).passCheck(Boolean.TRUE).build());
                }
            }
        }
        return flowNodeValidatedResultDTOS;
    }

}
